const queueMicrotask = window.queueMicrotask || (task => setTimeout(task))

class MyPromise {

    static PENDING = 'pending'
    static FULFILLED = 'fulfilled'
    static REJECTED = 'rejected'

    state = MyPromise.PENDING
    result = undefined
    reason = undefined
    onSuccess = null
    onFail = null
    onFinally = null

    constructor(exector) {

        const resolve = result => {
            const task = r => {
                this.state = MyPromise.FULFILLED
                this.result = r
                this.onSuccess && this.onSuccess(this.result)
            }
            result instanceof MyPromise ? result.then(task) : task(result)
        }

        const reject = reason => {
            const task = r => {
                this.state = MyPromise.REJECTED
                this.reason = r
                this.onFail && this.onFail(this.reason)
                this.reason && console.error('Uncaught (in promise)', this.reason)
            }
            reason instanceof MyPromise ? reason.then(task) : task(reason)
        }
        exector(resolve, reject)
    }

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => undefined
        onRejected = typeof onRejected === 'function' ? onRejected : () => undefined

        if (this.state === MyPromise.FULFILLED) {
            return new MyPromise(resolve => queueMicrotask(() => resolve(onFulfilled(this.result))))
        } else if (this.state === MyPromise.REJECTED) {
            return new MyPromise((resolve, reject) => queueMicrotask(() => reject(onRejected(this.reason))))
        } else if (this.state === MyPromise.PENDING) {
            return new MyPromise(resolve => {
                this.onSuccess = res => queueMicrotask(() => resolve(onFulfilled(res)))
                this.onFail = res => queueMicrotask(() => reject(onRejected(res)))
            })
        }
        return new MyPromise(resolve => resolve())
    }

    catch(onRejected) {
        onRejected = typeof onRejected === 'function' ? onRejected : () => undefined
        if (this.state === MyPromise.REJECTED) {
            return new MyPromise(resolve => resolve(onRejected(this.reason)))
        }
        return new MyPromise(resolve => resolve())
    }

    static resolve(value) {
        return new MyPromise(resolve => resolve(value))
    }

    static reject(reason) {
        return new MyPromise((resolve, reject) => reject(reason))
    }
}